package it.conv;

import java.io.PrintStream;
import java.util.*;

import static java.lang.Math.*;

public class DataGenerator {
	
	//Istanza del generatore di numeri casuali
	private static Random rand;
	
	//Potenza massima del segnale
	static final double es = 1.0;
	//Media della variabile casuale da cui generare rumore
	static final double media = 0.0;
	//Rapporto segnale / rumore per il generatore di rumore
	static double rapportoSn = 0.0;
	//Varianza del rumore
	static double var = 0.0;
	
	public static void inizializza(double esN0, int m, int out, PrintStream ps) {
		
		double preVar = var, preSn = rapportoSn;
		rand = new Random(System.currentTimeMillis());
		
	    /*
	     *  Dato il valore desiderato di Es/No (per BPSK, = Eb/No - 3 dB), si calcola la
	     *  deviazione standard del Rumore Gaussiano Bianco Additivo (AWGN). La 
	     *  deviazione standard del AWGN sarà usata per generare le variabili casuali
	     *  gaussiane che simulano il rumore aggiunto al segnale.
	     *  Il rapporto S/N viene pesato linearmente dal valore dei bit di uscita e
	     *  esponenzialmente (base 10) dal numero delle memorie utilizzate per aumentare
	     *  la varianza del rumore in caso di codici più efficenti
	     */
		
		rapportoSn = 2 * pow(10, (esN0 / (5 * m))) / out;
		var = sqrt(es / (rapportoSn * 2));
		
		if (abs(preVar - var) >= 0.01)
			ps.println("S/N = " + ConvApp.df2.format(rapportoSn));
		if (abs(preSn - rapportoSn) >= 0.01)
			ps.println("Varianza = " + ConvApp.df2.format(var));
		
	}
	
	//Generazione di un messaggio da codificare
	public static int[] generaDati(int l) {
		
		int[] outputArray = new int[l];
		for (int i = 0; i < outputArray.length; i++)
			outputArray[i] =  rand.nextInt(2);
		
		return outputArray;
	}
	
	//Procedure per la simulazione di canale
	public static double[] aggiungiRumore(int[] input, int lung_canale, boolean c) {
		
		double[] output	= new double[lung_canale];	
		 // Si trasformano i dati da 0/1 a +1/-1 e si aggiunge il rumore
		for (int i = 0; i < input.length; i++) {
			
			/*
			 * Se il valore binario è 1, il simbolo di canale è -1;
			 * in caso di 0 è +1 (trasformazione y = 1 - 2 * x).
			 */
			double segnale = 1 - 2 * (input[i]);
			
	        /* 
	         * Si genera il punto del rumore gaussiano, si aggiunge sui simboli di
	         * canale, e si manda in uscita i simboli di canale disturbati
	         */
			output[i] = segnale + ( (c) ? generaGauss() : 0 );
			
		}
		
		return output;
		
	}
	
	//Generatore di AWGN
	public static double generaGauss() {
		
	    /* 
	     * Si sfrutta il fatto che una variabile casuale R della distribuzione di Rayleigh,
	     * con distribuzione di probabilità F(R) = 0 se R < 0, è correlata a due variabili
	     * Gaussiane C e D attraverso la trasformazione C = R * cos(T) e D = R * sin(T),
	     * dove T è una variabile con distribuzione uniforme nell'intervallo (0, 2*PI).
	     */

		double u, r;
		
	    //Generazione di un numero casuale uniformemente distribuito u tra 0 e 1 - 1E-6
		u = rand.nextDouble();
		
		// Generazione di un numero casuale con distribuzione di Rayleigh r usando u 
		r = var * sqrt( 2.0 * -log( (1.0 - u) ) );
		
		// Generazione di un numero casuale uniformemente distribuito u come prima
		u = rand.nextDouble();
		
		return (media + r * cos( 2 * PI * u));
	}
	
}
